#region imported Namespaces
using System;
using System.Collections;
using System.Data;
using System.Data.SqlClient;

using BO = PHSRAG.Insight21.BusinessObjects;
using Utility = PHSRAG.Utility;
using ICachePolicy = PHSRAG.Insight21.Policy.ICachePolicy;
using DataAccess = PHSRAG.Utility.DataAccess;
#endregion

namespace PHSRAG.Insight21.ScriptEngine
{
	/// <summary>
	/// Summary description for Script.
	/// </summary>
	public class Script : BO.SmartBusinessObject, ICloneable
	{
		#region Constants
		private const string ScriptKey = "Script_0AF471F6-3F6F-4ff2-9B14-AC1AAAF77E03";
		private const string ScriptDataTableName = "Script";
		private const string ScriptReferencesDataTableName = "ScriptReferences";

		/// <summary>
		///  Script dataTable columns
		/// </summary>
		private const string columnId = "Id";
		private const string columnName = "Name";
		private const string columnScriptText = "ScriptText";
		private const string columnType = "Type";
		#endregion

		#region Members
		private ArrayList scriptRefs;
		private string name;
		//private string relateClass;
		//private int    relateId;
		private string type;
		private string scriptText;
		private bool markForDeletion;

		/// <summary>
		/// public collection of the DLL references a script needs to run.
		/// </summary>
		public ArrayList ScriptReferences
		{
			get 
			{	if ( scriptRefs == null )
					GetScriptReferences();
				return scriptRefs; 	
			}
			set { scriptRefs = value; }
		}
		#endregion

		#region Constructor
		/// <summary>
		/// Constructs a new Script object based on the cachepolicy passed in.
		/// </summary>
		/// <param name="cachePolicy">Cachepolicy where the script will do its caching</param>
		/// <param name="newScript">flag whether this script is created as a new one (true) 
		/// or a script loaded previously from teh database (false)</param>
		public Script(ICachePolicy cachePolicy) : base(cachePolicy)
		{
        }

		public Script(ICachePolicy cachePolicy, DataRow dataRow ) : base(cachePolicy)
		{
			// grab the row data here.
			Id			= (int)dataRow[columnId];
			name		= (string)dataRow[columnName];
			//relateClass = (string)dataRow[columnRelateClass];
			//relateId	= (int)dataRow[columnRelateId];
			type		= (string)dataRow[columnType];
			scriptText	= (string)dataRow[columnScriptText];
		}


		#endregion

        #region ICloneable
        object ICloneable.Clone()
        {
            return Clone();
        }
        #endregion

        public Script Clone()
        {
            Script script = (Script)MemberwiseClone( );
            script.Id = 0;
            script.Dirty = true;

            return script;
        }

        #region Properties
		/// <summary>
		/// Cache Key uniquely identifies this object within the cache.
		/// </summary>
		/// <returns>the unique string for this object.</returns>
		public override string CacheKey()
		{
			return ScriptKey;
		}

		public string Name
		{
			get {  	return name; }
			set {  SetDirty( ref name, value);	}
		}

		/// <summary>
		/// Body of the script
		/// </summary>
		public string ScriptText
		{
			get { return scriptText; }
			set { SetDirty( ref scriptText, value); }
		}

		/// <summary>
		/// Type of the script, from ref code tables. Note that this is NOT 'VB' or 'JavaScript', but the
		/// type as it relates to Partners (eIRB validation script, eIRB Requirement script, etc.)
		/// </summary>
		public string Type
		{
			get { return type; }
			set { SetDirty( ref type, value); }
		}

//		/// <summary>
//		/// ClassName the script is contained by / related to
//		/// </summary>
//		public string RelateClass
//		{
//			get { return relateClass;}
//			set { SetDirty( ref relateClass, value); }
//		}
//
//		/// <summary>
//		/// Object instance Id of the object the script is related to.
//		/// </summary>
//		public int RelateId
//		{
//			get { return relateId; }
//			set { SetDirty( ref relateId, value); }
//		}
		public bool MarkedForDeletion
		{
			get { return markForDeletion; }
			set { SetDirty( ref markForDeletion, value); }
		}
		#endregion

		#region Caching
		/// <summary>
		/// Cache this instance using the prevalent cache policy.
		/// </summary>
		public override void Cache()
		{
			CachePolicy.Cache(CacheKey(), this);
		}

		/// <summary>
		/// Remove this instance from the cache using the prevalent cache policy.
		/// </summary>
		public override void UnCache()
		{
			CachePolicy.UnCache(CacheKey());
		}
		#endregion

		#region Saveable Business Object Methods

		/// <summary>
		/// DeleteObject is not currently implemented in the Script object. 
		/// If called, it will throw an exception
		/// </summary>
		/// <param name="connectionString">the connectionstring to the database</param>
		/// <param name="sqlTransaction">SQL transaction to use when deleting.</param>
		protected override void DeleteObject( SqlTransaction sqlTransaction)
		{
			DataAccess.ExecuteNonQuery( sqlTransaction.Connection, sqlTransaction, "DeleteScript", DataAccess.BuildSqlParameterArray(
				"@id", SqlDbType.Int, Id));
		}


		/// <summary>
		/// SaveObject always saves 
		/// </summary>
		protected override void SaveObject( SqlTransaction transaction )
		{
			// the stored procedure will determine whether to insert or update an existing Script object.
			object idObject = DataAccess.ExecuteScalar( transaction.Connection, transaction, "SaveScript", DataAccess.BuildSqlParameterArray(
				"@id", SqlDbType.Int, Id,
				"@name", SqlDbType.VarChar, Name,
				"@scriptText", SqlDbType.VarChar, ScriptText,
				"@type", SqlDbType.VarChar, Type ));

			// set the ID to the returned object
			int test = Convert.ToInt32 (idObject);
			Id = test;
		}


		/// <summary>
		/// Determine whether or not the script is dirty or not, based  both on
		/// whether is's properties have changed (making it dirty), or any of its 
		/// contained business objects are dirty also.
		/// </summary>
		/// <returns>true if it is dirty, false if not dirty.</returns>
		public override bool IsDirty()
		{
			if ( Dirty )
				return true;
			else return false;

			// check Script References?
		}

		/// <summary>
		/// do nothing here, since there are currently no 
		/// business validation rules for Scripts.
		/// </summary>
		public override void Validate()
		{
			// do nothing here
		}


		/// <summary>
		///  Loads the Script based on the connection string and the optional Args.
		/// </summary>
		/// <param name="connectionString"></param>
		/// <param name="args"></param>
		public override void Load ( string connectionString, params object[] args )
		{
			if ( args.Length != 1 )
				throw new Exception("Script load expects script Id as a parameter.");

			try
			{
				DataSet newDataSet = new DataSet();
				Utility.DataAccess.LoadDataSet(newDataSet, ScriptDataTableName, connectionString, "GetScript", 
					Utility.DataAccess.BuildSqlParameterArray(
					//											"@type", SqlDbType.VarChar, (string)args[(int)ScriptLoadOrder.ScriptType],
					"@scriptId", SqlDbType.Int, (int)args[0]));
				
				DataTable table = newDataSet.Tables[ScriptDataTableName];
				DataRow dataRow = table.Rows[0];

				// set the properties.
				Id			= (int)dataRow[columnId];
				name		= (string)dataRow[columnName];
				type		= (string)dataRow[columnType];
				scriptText	= (string)dataRow[columnScriptText];
			}
			catch( Exception e)
			{
				Utility.ExceptionReport.WriteToEventLog ("Script Load", e, true, Utility.ExceptionReport.ReportType.Error);
				throw;
			}
		}
		#endregion

		#region ScriptReference Method
		/// <summary>
		/// GetScriptReferences loads the private scriptRefs arrayList member
		/// with all script references belonging to the script.
		/// </summary>
		public void GetScriptReferences ( )
		{
				scriptRefs = new ArrayList();
				DataSet ScriptRef = new DataSet();

				try
				{
					// Load the data.
					Utility.DataAccess.LoadDataSet(ScriptRef, ScriptReferencesDataTableName, Utility.DataAccess.GetConnectionString(), "GetScriptReferences", 
						Utility.DataAccess.BuildSqlParameterArray("@scriptId" ,SqlDbType.Int, this.Id));
					
					// check if there's something to load..
					if( ScriptRef.Tables[ScriptReferencesDataTableName] != null )
					{
						foreach (DataRow row in ScriptRef.Tables[ScriptReferencesDataTableName].Rows )
						{
							ScriptReference newScriptReference = new ScriptReference ( row );
							scriptRefs.Add ( newScriptReference );
						}
					}
				}
				catch( Exception e)
				{
					Utility.ExceptionReport.WriteToEventLog ("Script References Load", e, true, Utility.ExceptionReport.ReportType.Error);
					throw;
				}

				
		}
		#endregion

		#region Static Get methods
		/// <summary>
		/// Load scripts based on the type specified.
		/// </summary>
		/// <param name="scriptType">Script type, from SCRIPT TYPE reference Domain.</param>
		/// <returns>Collection of script objects with the specified type</returns>
		public static ArrayList GetScriptsByType ( string scriptType, ICachePolicy cachePolicy )
		{
			try
			{
				ArrayList scripts = new ArrayList();
				DataSet scriptsDataSet = Utility.DataAccess.GetDataSet(Utility.DataAccess.GetConnectionString(), "GetScriptsByType",
					Utility.DataAccess.BuildSqlParameterArray("@type", SqlDbType.VarChar, scriptType));

				if (scriptsDataSet.Tables.Count > 0)
				{
					foreach( DataRow row in scriptsDataSet.Tables[0].Rows )
					{
						Script script = new Script(cachePolicy, row);
						scripts.Add (script);
					}
				}
				else
				{
					throw new Exception("GetScriptsByType stored procedure returned incorrect results."); 
				}
				return scripts;
			}
			catch(Exception ex)
			{
				throw new Exception("Script.GetScriptsByType: Failed to load Scripts.", ex);
			}
		}
			
		/// <summary>
		/// Overload of GetScriptsByType which takes a connection string. This will be used when callign
		/// via a webservice or somewhere else where a connstring is passed in.
		/// </summary>
		/// <param name="connectionString">connection string to the DB</param>
		/// <param name="scriptType">the type of script to be loaded</param>
		/// <param name="cachePolicy">relevant cachepolicy</param>
		/// <returns>and array list of scripts of the given type. Will return an empty list if there are none.</returns>
		public static ArrayList GetScriptsByType ( string connectionString, string scriptType, ICachePolicy cachePolicy )
		{
			try
			{
				ArrayList scripts = new ArrayList();
				DataSet scriptsDataSet = Utility.DataAccess.GetDataSet(connectionString, "GetScriptsByType",
					Utility.DataAccess.BuildSqlParameterArray("@type", SqlDbType.VarChar, scriptType));

				if (scriptsDataSet.Tables.Count > 0)
				{
					foreach( DataRow row in scriptsDataSet.Tables[0].Rows )
					{
						Script script = new Script(cachePolicy, row);
						scripts.Add (script);
					}
				}
				else
				{
					throw new Exception("GetScriptsByType stored procedure returned incorrect results."); 
				}
				return scripts;
			}
			catch(Exception ex)
			{
				throw new Exception("Script.GetScriptsByType: Failed to load Scripts.", ex);
			}
		}

		/// <summary>
		/// Get SCript loads a single script based on the ID, it is a wrapper for the Load method.
		/// </summary>
		/// <param name="scriptId">Id of the script to be loaded.</param>
		/// <param name="cachePolicy">the current cachepolicy</param>
		/// <returns>a script object hopefully loaded.</returns>
		public static Script GetScript ( int scriptId, ICachePolicy cachePolicy )
		{
			Script script = new Script (cachePolicy);

			script.Load ( Utility.DataAccess.GetConnectionString(), scriptId );

			return script;
		}
#endregion	
//		public enum ScriptLoadOrder
//		{
//			RelateClass,
//			RelateId,
//			ScriptType
//		}
	}
}
